/**
* Copyright @ 2023 iAuto (Shanghai) Co., Ltd.
* All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are NOT permitted except as agreed by
* iAuto (Shanghai) Co., Ltd.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*/

//
import ArgumentTypes from '@scene/scene-vm/src/extension-support/argument-type';

import BlockType from '@scene/scene-vm/src/extension-support/block-type';

// TODO: grow this until it can fully replace `_convertForScratchBlocks` in the VM runtime
const defineDynamicBlock = (ScratchBlocks, categoryInfo, staticBlockInfo, extendedOpcode) => ({
  domToMutation: function (xmlElement) {
    const blockInfoText = xmlElement.getAttribute('blockInfo');
    if (!blockInfoText) return;
    if (!this.needsBlockInfoUpdate) {
      throw new Error('Attempted to update block info twice');
    }
    delete this.needsBlockInfoUpdate;
    this.blockInfoText = blockInfoText;
    const blockInfo = JSON.parse(blockInfoText);

    switch (blockInfo.blockType) {
      case BlockType.REPORTER:
        this.setOutput(true);
        this.setOutputShape(ScratchBlocks.OUTPUT_SHAPE_ROUND);
        if (!blockInfo.disableMonitor) {
          this.setCheckboxInFlyout(true);
        }
        break;
      case BlockType.COMMAND:
      case BlockType.CONDITIONAL:
      case BlockType.LOOP:
        this.setOutputShape(ScratchBlocks.OUTPUT_SHAPE_SQUARE);
        this.setPreviousStatement(true);
        this.setNextStatement(!blockInfo.isTerminal);
        break;
      case BlockType.HAT:
      case BlockType.EVENT:
        this.setOutputShape(ScratchBlocks.OUTPUT_SHAPE_SQUARE);
        this.setNextStatement(true);
        break;
      case BlockType.BOOLEAN:
        this.setOutput(true);
        this.setOutputShape(ScratchBlocks.OUTPUT_SHAPE_HEXAGONAL);
        break;
    }

    if (blockInfo.color1 || blockInfo.color2 || blockInfo.color3 || blockInfo.color4) {
      // `setColour` handles undefined parameters by adjusting defined colors
      this.setColour(blockInfo.color1, blockInfo.color2, blockInfo.color3, blockInfo.color4);
    }

    // Layout block arguments
    // TODO handle E/C Blocks
    const blockText = blockInfo.text;
    const args = [];
    let argCount = 0;
    const scratchBlocksStyleText = blockText.replace(/\[(.+?)]/g, (match, argName) => {
      const arg = blockInfo.arguments[argName];
      switch (arg.type) {
        
        case ArgumentTypes.BOOLEAN:
          args.push({ type: 'input_value', name: argName, check: 'Boolean' });
          break;
          case ArgumentTypes.STRING:
          args.push({ type: 'input_value', name: argName });
          break;
      }
      return `%${++argCount}`;
    });
    this.interpolate_(scratchBlocksStyleText, args);
  },

  init: function () {
    const blockJson = {
      type: extendedOpcode,
      inputsInline: true,
      category: categoryInfo.name,
      colour: categoryInfo.color1,
      colourSecondary: categoryInfo.color2,
      colourTertiary: categoryInfo.color3,
      colourStroke: categoryInfo.color4
    };
    // There is a scratch-blocks / Blockly extension called "scratch_extension" which adjusts the styling of
    // blocks to allow for an icon, a feature of Scratch extension blocks. However, Scratch "core" extension
    // blocks don't have icons and so they should not use 'scratch_extension'. Adding a scratch-blocks / Blockly
    // extension after `jsonInit` isn't fully supported (?), so we decide now whether there will be an icon.
    if (staticBlockInfo.blockIconURI || categoryInfo.blockIconURI) {
      blockJson.extensions = ['scratch_extension'];
    }
    // initialize the basics of the block, to be overridden & extended later by `domToMutation`
    this.jsonInit(blockJson);
    // initialize the cached block info used to carry block info from `domToMutation` to `mutationToDom`
    this.blockInfoText = '{}';
    // we need a block info update (through `domToMutation`) before we have a completely initialized block
    this.needsBlockInfoUpdate = true;
  },


  mutationToDom: function () {
    const container = document.createElement('mutation');
    container.setAttribute('blockInfo', this.blockInfoText);
    return container;
  },
});

export default defineDynamicBlock;
